import os
from fastapi import FastAPI, Request
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from fastapi.middleware.cors import CORSMiddleware
from logs.logger import slogger
from config.main import server
from config import main as config
from sqlalchemy import create_engine, inspect
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base
from starlette.staticfiles import StaticFiles
from contextlib import contextmanager

app = FastAPI(
    title=server.TITLE,
    description=server.DESCRIPTION,
    version=server.LEVEL,
    docs_url=config.server.DOC_URL,
    redoc_url=config.server.RE_DOC_URL,
)
app.add_middleware(TrustedHostMiddleware, allowed_hosts=server.ALLOWED_HOSTS)
app.add_middleware(CORSMiddleware, allow_origins=server.ALLOWED_ORIGINS, allow_credentials=server.ALLOWED_CREDENTIALS,
                   allow_methods=server.ALLOWED_METHODS, allow_headers=server.ALLOWED_HEADERS)
if server.TURN_ON_STATIC:
    # 如果没有创建静态文件目录，则创建
    if not os.path.exists(server.STATIC_NAME):
        os.mkdir(server.STATIC_NAME)
    app.mount(server.STATIC_PATH, StaticFiles(directory=server.STATIC_NAME),
              name=server.STATIC_NAME)

# 自定义 Middleware 自动记录请求信息
@app.middleware("http")
async def log_requests(request: Request, call_next):
    slogger.info(f"请求 【{request.method}】 | {request.url}")
    response = await call_next(request)
    return response


# 根据选择的数据库类型来构建连接字符串
if config.db.db_type == config.DatabaseType.MYSQL:
    db_url = f"mysql+pymysql://{config.db.username}:{config.db.password}@{config.db.server}/{config.db.database}"
elif config.db.db_type == config.DatabaseType.POSTGRESQL:
    db_url = f"postgresql://{config.db.username}:{config.db.password}@{config.db.server}/{config.db.database}"
elif config.db.db_type == config.DatabaseType.SQLITE:
    db_url = f"sqlite:///{config.db.database}.sqlite"
else:
    raise ValueError("Unsupported database type")

# 配置数据库地址：数据库类型+数据库驱动名称://用户名:密码@机器地址:端口号/数据库名
engine = create_engine(db_url, encoding='utf-8')
inspector = inspect(engine)
# 把当前的引擎绑定给这个会话；
# autocommit：是否自动提交 autoflush：是否自动刷新并加载数据库 bind：绑定数据库引擎
Session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)  #
# 实例化
session = Session()
# 会话工厂 -- 是一个会话，可以通过它来进行数据库的操作
# sessionDB = scoped_session(Session)
#
# sessionDB.configure(autocommit=True, expire_on_commit=False)

Base = declarative_base()


def get_db() -> Session:
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()


def db_commit(db):
    try:
        db.commit()
    except:
        db.rollback()
        raise
    finally:
        db.close()


def serialize(model):
    # 将model转换为dict，序列化
    # 这个主要用于使用session_scope()时，将查询结果转换为dict
    return {c.name: getattr(model, c.name) for c in model.__table__.columns}


@contextmanager
def session_scope():
    """
    会话管理器
    下面的yield session2相当于return session2
        with session_scope() as session2:
            info = session2.query(Tbluser).all()
            data = [serialize(row) for row in info]
            print(data)
            return {'data': data, 'code': '0000', 'message': 'success'}
    """
    session2 = Session()
    try:
        yield session2
        session2.commit()
    except:
        session2.rollback()
        raise
    finally:
        session2.close()


# 获取当前脚本所在目录
current_dir = os.path.dirname(os.path.abspath(__file__))
# 动态添加路由
for root, dirs, files in os.walk(current_dir):
    for file in files:
        # 如果文件名为views.py，则尝试导入router
        if file == 'views.py':
            try:
                module_path = os.path.join(root, file).replace('/', '.').replace('\\', '.')[:-3]
                path = module_path.split('.apps')[-1]  # 获取apps之后的路径
                module = __import__(f'apps{path}', fromlist=['router'])
                app.include_router(module.router)
            except:
                pass
